package gov.nasa.anml.parsing;

import org.antlr.runtime.*;

import gov.nasa.anml.utility.SimpleString;

import java.io.Serializable;

import javax.print.DocFlavor.INPUT_STREAM;

import org.antlr.runtime.tree.*;

public class ANMLToken extends BaseTree implements Token, Serializable {

// token imp
	
	public int type=INVALID_TOKEN_TYPE;
	public int line;
	public int charPositionInLine = -1; // set to invalid position
	public int channel=DEFAULT_CHANNEL;
	public transient ANMLCharStream input;

	/** We need to be able to change the textSimple once in a while.  If
	 *  this is non-null, then getText should return this.  Note that
	 *  start/stop are not affected by changing this.
	  */
	public String text;
	public SimpleString textSimple;
	
	/** What token number is this from 0..n-1 tokens; < 0 implies invalid index */
	public int index = -1;

	/** The char position into the input buffer where this token starts */
	public int start;

	/** The char position into the input buffer where this token stops */
	public int stop;

	public ANMLToken(int type) {
		this.type = type;
	}

	public ANMLToken(CharStream input, int type, int channel, int start, int stop) {
		this.input = (ANMLCharStream) input;
		this.type = type;
		this.channel = channel;
		this.start = start;
		this.stop = stop;
	}

	public ANMLToken(int type, String text) {
		this.type = type;
		this.text = text;
		if (text != null)
			this.textSimple = new SimpleString(text);
	}

	public ANMLToken(Token oldToken) {
		if (oldToken == null)
			return;
		text = oldToken.getText();
		if (text != null)
			textSimple = new SimpleString(text);
		type = oldToken.getType();
		line = oldToken.getLine();
		index = oldToken.getTokenIndex();
		charPositionInLine = oldToken.getCharPositionInLine();
		channel = oldToken.getChannel();
		input = (ANMLCharStream) oldToken.getInputStream();
		if ( oldToken instanceof ANMLToken ) {
			ANMLToken old = (ANMLToken) oldToken;
			start = old.start;
			stop = old.stop;
			this.startIndex = old.startIndex;
			this.stopIndex = old.stopIndex;
		}
	}

	public int getType() {
		return type;
	}

	public void setLine(int line) {
		this.line = line;
	}

	public String getText() {
		if ( textSimple!=null ) {
			if (text == null)
				text = new String(textSimple.v);
			return text;
		}
		if ( input==null ) {
			return null;
		}
		textSimple = input.makeSimpleString(start,stop);
		text = new String(textSimple.v);
		return text;
	}
	
	public SimpleString getSimpleText() {
		if ( textSimple!=null ) {
			return textSimple;
		}
		if ( input==null ) {
			return SimpleString.Empty;
		}
		textSimple = input.makeSimpleString(start,stop);
		return textSimple;
	}
	
	public String toPositionString() {
		return "_l" + line + "_c" + (charPositionInLine+1);
	}

	/** Override the textSimple for this token.  getText() will return this textSimple
	 *  rather than pulling from the buffer.  Note that this does not mean
	 *  that start/stop indexes are not valid.  It means that that input
	 *  was converted to a new string in the token object.
	 */
	public void setText(String text) {
		this.text = text;
		if (text != null) {
			if (textSimple == null) {
				this.textSimple = new SimpleString(text);
			} else {
				this.textSimple.length=0;
			}
		}
	}

	public int getLine() {
		return line;
	}

	public int getCharPositionInLine() {
		return charPositionInLine;
	}

	public void setCharPositionInLine(int charPositionInLine) {
		this.charPositionInLine = charPositionInLine;
	}

	public int getChannel() {
		return channel;
	}

	public void setChannel(int channel) {
		this.channel = channel;
	}

	public void setType(int type) {
		this.type = type;
	}

	public int getStartIndex() {
		return start;
	}

	public void setStartIndex(int start) {
		this.start = start;
	}

	public int getStopIndex() {
		return stop;
	}

	public void setStopIndex(int stop) {
		this.stop = stop;
	}

	public int getTokenIndex() {
		return index;
	}

	public void setTokenIndex(int index) {
		this.index = index;
	}

	public CharStream getInputStream() {
		return input;
	}

	public void setInputStream(CharStream input) {
		this.input = ((ANMLCharStream)input);
		// will break loudly and ungracefully if wrong input is provided
	}
	
	public void setInputStream(ANMLCharStream input) {
		this.input = input;
	}


	public String toString() {
		return getText();
		/*
		String channelStr = "";
		if ( channel>0 ) {
			channelStr=",channel="+channel;
		}
		String txt = getText();
		if ( txt!=null ) {
			txt = txt.replaceAll("\n","\\\\n");
			txt = txt.replaceAll("\r","\\\\r");
			txt = txt.replaceAll("\t","\\\\t");
		}
		else {
			txt = "<no text>";
		}
		return "[@"+getTokenIndex()+","+start+":"+stop+"='"+txt+"',<"+type+">"+channelStr+","+line+":"+getCharPositionInLine()+"]";
		*/
	}
	
// tree imp
	protected int startIndex=-1, stopIndex=-1;

	/** Who is the parent node of this node; if null, implies node is root */
	public ANMLToken parent;

	/** What index is this node in the child list? Range: 0..n-1 */
	public int childIndex = -1;

	public ANMLToken() { 
		
	}
	
	public ANMLToken getToken() {
		return this;
	}

	public ANMLToken dupNode() {
		return new ANMLToken(this);
	}

	public boolean isNil() {
		return type==INVALID_TOKEN_TYPE;
	}

	public int getTokenStartIndex() {
		return startIndex;
	}

	public void setTokenStartIndex(int index) {
		startIndex = index;
	}

	public int getTokenStopIndex() {
		return stopIndex;
	}

	public void setTokenStopIndex(int index) {
		stopIndex = index;
	}

	public int getChildIndex() {
		return childIndex;
	}

	public ANMLToken getParent() {
		return parent;
	}

	public void setParent(ANMLToken t) {
		this.parent = (ANMLToken)t;
	}

	public void setChildIndex(int index) {
		this.childIndex = index;
	}
	
	public static StringBuilder newlineAndIndent = new StringBuilder("");
	public static StringBuilder buf = new StringBuilder();
	
    public String toStringTree() {
    	if (children == null || children.size() == 0)
    		return this.toString();
    	newlineAndIndent.setLength(1);
    	newlineAndIndent.append('\n');
    	buf.setLength(0);
    	if (!isNil()) {
    		buf.append('(');
    		buf.append(this.toString());
    		newlineAndIndent.append('\t');
    	} 
    	ANMLToken t; int i=0; do {
    		t = (ANMLToken) children.get(i);
    		buf.append(' ');
    		t._toStringTree(buf);
    	} while (++i < children.size());
    	if (!isNil()) {
    		buf.append("\n)\n");
    	} 
    	return buf.toString();
/*		if ( children==null || children.size()==0 ) {
			return prefix+this.toString();
		}
		StringBuffer buf = new StringBuffer();
		char sep = ' ';
		if ( !isNil() ) {
			sep = '\n';
			buf.append(prefix).append("(");
			buf.append(this.toString());
			buf.append(sep);
			prefix.append('\t');
		}
		BaseTree t;
		if (children != null && children.size() > 0) {
			t = (BaseTree) children.get(0);
			buf.append(t.toStringTree());
			for (int i = 1; i < children.size(); i++) {
				t = (BaseTree) children.get(i);
				buf.append(sep);
				buf.append(t.toStringTree());
			}
		}
		if ( !isNil() ) {
			buf.append(")");
			prefix.setLength(prefix.length()-1);
		}
		return buf.toString();*/
	}
    
    public void _toStringTree(StringBuilder buf) {
		if ( children==null || children.size()==0 ) {
			buf.append(this.toString());
			return;
		}
		CharSequence sep = " "; 
		if ( !isNil() ) {
//			if (children.size() > 3)
//				sep = newlineAndIndent;
			buf.append(newlineAndIndent).append("(");
			buf.append(this.toString());
			newlineAndIndent.append('\t');
		}
		ANMLToken t;
		int i=0;
		do {
			t = (ANMLToken) children.get(i);
			buf.append(sep);
			t._toStringTree(buf);
		} while (++i < children.size());
		if ( !isNil() ) {
			newlineAndIndent.setLength(newlineAndIndent.length()-1);
			buf.append(')');
			//if (children.size() > 3)
		}
	}

}
